#include <linux/init.h>
#include <linux/module.h>

#include <linux/seq_file.h> /* seq_file ... */
#include <linux/proc_fs.h>  /* para /proc */


/* vamos a crear la entrada /proc/hackcount */
#define PROC_NAME "hackcount"

MODULE_LICENSE("Dual BSD/GPL");


/* Esta variable va a mantener una cuenta. Comenzamos en 0
 * La idea es que cada vez que se imprime, se incrementa el contador */

static int count = 0;


/*
 * Se recomienda usar la interfaz seq_file para imprimir al /proc
 *
 * Esto debido a que de otra forma es como engorroso. Pueden mirar en otros
 * lados del kernel para que vean que es mas facil asi
 *
 * TODO: decir en que archivo se puede mirar todo lo que uno se ahorra usando seq_file
 *
 * http://lwn.net/Articles/22355/
 * http://tldp.org/LDP/lkmpg/2.6/html/x861.html
 *
 * Con seq_file uno escribe como si estuviera escribiendo a un archivo.
 *
 */

static int proc_count_show(struct seq_file *m, void *v)
{
  seq_printf(m, "%d\n", count++);
  return 0;
}

/* esta se llama cuando uno abre el archivo ... */
/* TODO: poner un printk que avise cuando se llama esta funcion */

static int proc_count_open(struct inode *inode, struct file *file)
{
  printk(KERN_ALERT "llamada a proc_count_open()\n");
  return single_open(file, proc_count_show, NULL);
}


/*
 * esta notacion esta descrita en:
 * http://wiki.freaks-unidos.net/linux%20drivers#sintaxis-de-inicializaci-n-c-tagged-structures
 */
static struct file_operations proc_count_operations = {
  .open     = proc_count_open,
  .read     = seq_read,
  .llseek   = seq_lseek,
  .release  = single_release,
};

/* funcion de inicializacion */
static int count_init(void)
{
 struct proc_dir_entry *e;

 /* esta es la funcion que crea el archivo "virtual" /proc/PROC_NAME */
 e = create_proc_entry(PROC_NAME, 0, NULL);

 if (e)
   e->proc_fops = &proc_count_operations;
 else
   printk(KERN_ERR "/proc/" PROC_NAME " no se pudo crear :-( \n");

  return 0;
}

/* lo borramos al salir */
static void count_exit(void)
{
  printk(KERN_ALERT "removiendo la entrada en /proc/%s\n", PROC_NAME);
  remove_proc_entry(PROC_NAME, NULL);
}

module_init(count_init);
module_exit(count_exit);
